package com.github.springbootPlus.excel.parsing;


import com.github.springbootPlus.excel.ExcelDefinitionReader;
import com.github.springbootPlus.excel.config.FieldValue;
import com.github.springbootPlus.excel.exception.ExcelException;
import com.github.springbootPlus.excel.util.*;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;

import java.io.*;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;

/**
 * Excel抽象解析器
 *
 * @author lisuo
 */
public abstract class AbstractExcelResolver implements CellValueConverter {

    protected ExcelDefinitionReader definitionReader;

    /**
     * 注册字段解析信息
     */
    protected Map<String, CellValueConverter> cellValueConverters = new HashMap<String, CellValueConverter>();

    protected Map<String, RowFilter> rowFilterCache = new HashMap<String, RowFilter>();

    public AbstractExcelResolver(ExcelDefinitionReader definitionReader) {
        this.definitionReader = definitionReader;
    }

    /**
     * 解析表达式format 属性
     *
     * @param value
     * @param format
     * @param fieldValue
     * @param rowNum
     * @return
     */
    protected String resolverExpression(String value, String format, Type type, FieldValue fieldValue, int rowNum) {
        try {
            String[] expressions = StringUtils.split(format, ",");
            for (String expression : expressions) {
                String[] val = StringUtils.split(expression, ":");
                String v1 = val[0];
                String v2 = val[1];
                if (Type.EXPORT == type) {
                    if (value.equals(v1)) {
                        return v2;
                    }
                } else if (Type.IMPORT == type) {
                    if (value.equals(v2)) {
                        return v1;
                    }
                }
            }
        } catch (Exception e) {
            throw new ExcelException(getErrorMsg(fieldValue, "表达式:" + format + "错误,正确的格式应该以[,]号分割,[:]号取值", rowNum));
        }
        throw new ExcelException(getErrorMsg(fieldValue, getResolveMsg(value, format), rowNum));
    }

    private String getResolveMsg(String value, String format) {
        String[] expressions = StringUtils.split(format, ",");
        String[] values = new String[expressions.length];
        int i = 0;
        for (String expression : expressions) {
            String[] val = StringUtils.split(expression, ":");
            values[i++] = val[1];
        }
        return "[" + value + "]取值错误,取值范围是[" + StringUtils.join(values, ",") + "]";
    }

    /**
     * 设置Cell单元的值
     *
     * @param cell
     * @param value
     */
    protected void setCellValue(Cell cell, Object value) {
        ExcelUtil.setCellValue(cell, value);
    }

    /**
     * 获取cell值
     *
     * @param cell
     * @return
     */
    protected Object getCellValue(Cell cell) {
        return ExcelUtil.getCellValue(cell);
    }

    //默认实现
    @Override
    public Object convert(Object bean, Object value, FieldValue fieldValue, Type type, int rowNum) throws Exception {
        //解析器实现，读取数据
        String convName = fieldValue.getCellValueConverterName();
        if (convName == null) {
            //执行默认转化：字符转数字，转日期
            return (StringUtils.isBlank(Objects.toString(value))) ? null : normalConvert(value, fieldValue, type, rowNum);
        } else {
            //自定义
            CellValueConverter conv = cellValueConverters.get(convName);
            if (conv == null) {
                synchronized (this) {
                    if (conv == null) {
                        conv = getBean(convName);
                        cellValueConverters.put(convName, conv);
                    }
                }
                conv = cellValueConverters.get(convName);
            }
            return conv.convert(bean, value == null ? "" : value.toString(), fieldValue, type, rowNum);
        }
    }


    protected boolean checkRow(String rowFilter, Object bean, RowFilter.Type type, int rowNum, List<ExcelError> errors) throws Exception {
        //自定义
        RowFilter filter = rowFilterCache.get(rowFilter);
        if (filter == null) {
            synchronized (this) {
                if (filter == null) {
                    filter = getRowFilterBean(rowFilter);
                    rowFilterCache.put(rowFilter, filter);
                }
            }
            filter = rowFilterCache.get(rowFilter);
        }
        return filter.isValid(bean, type, rowNum, errors);
    }

    /**
     * 常规处理：日期、数值转换、字典数据转换
     *
     * @param value
     * @param fieldValue
     * @param type
     * @param rowNum
     * @return
     * @throws ParseException
     */
    private Object normalConvert(Object value, FieldValue fieldValue, Type type, int rowNum) throws ParseException {
        String name = fieldValue.getName();
        String pattern = fieldValue.getPattern();
        String format = fieldValue.getFormat();
        DecimalFormat decimalFormat = fieldValue.getDecimalFormat();

        //日期型数据处理
        if (StringUtils.isNotBlank(pattern)) {
            String[] patterns = StringUtils.delimitedListToStringArray(pattern, ",");
            if (Type.EXPORT == type) {
                //导出使用第一个pattern
                return DateUtils.date2Str((Date) value, patterns[0]);
            } else if (Type.IMPORT == type) {
                if (value instanceof String) {
                    Date date = DateUtils.tryStr2Date((String) value, patterns);
                    if (date == null) {
                        StringBuilder errMsg = new StringBuilder("[");
                        errMsg.append(value.toString()).append("]")
                                .append("不能转换成日期,正确的格式应该是:[").append(pattern + "]");
                        throw new ExcelException(getErrorMsg(fieldValue, errMsg.toString(), rowNum));
                    }
                    return date;
                } else if (value instanceof Date) {
                    return value;
                } else if (value instanceof Number) {
                    Number val = (Number) value;
                    return new Date(val.longValue());
                } else {
                    throw new ExcelException(getErrorMsg(fieldValue, "数据格式错误,[ " + name + " ]的类型是:" + value.getClass() + ",无法转换成日期", rowNum));
                }
            }
        }

        //字典项数据的处理
        if (format != null) {
            return resolverExpression(value.toString(), format, type, fieldValue, rowNum);
        }

        //数字型处理
        if (decimalFormat != null) {
            if (Type.IMPORT == type) {
                if (value instanceof String) {
                    return decimalFormat.parse(value.toString());
                }
            } else if (Type.EXPORT == type) {
                if (value instanceof String) {
                    value = ConvertUtils.convert(value, BigDecimal.class);
                }
                return decimalFormat.format(value);
            }
        }

        return value;

    }

    //获取bean
    protected CellValueConverter getBean(String convName) throws ClassNotFoundException {
        CellValueConverter bean = null;
        if (SpringUtils.isInited()) {
            bean = (CellValueConverter) SpringUtils.getBean(Class.forName(convName));
        } else {
            bean = (CellValueConverter) ReflectUtils.newInstance(Class.forName(convName));
        }
        return bean;
    }

    //获取bean
    protected RowFilter getRowFilterBean(String filterName) throws ClassNotFoundException {
        RowFilter bean = null;
        if (SpringUtils.isInited()) {
            bean = (RowFilter) SpringUtils.getBean(Class.forName(filterName));
        } else {
            bean = (RowFilter) ReflectUtils.newInstance(Class.forName(filterName));
        }
        return bean;
    }

    /**
     * 获取错误消息
     *
     * @param fieldValue
     * @param errMsg     消息提示内容
     * @param rowNum
     * @return
     */
    protected String getErrorMsg(FieldValue fieldValue, String errMsg, int rowNum) {
        StringBuilder err = new StringBuilder();
        err.append("第[").append(rowNum).append("行],[")
                .append(fieldValue.getTitle()).append("]").append(errMsg);
        return err.toString();
    }

    /**
     * @className: FileUtils
     * @description:
     * @author: WANGHUI
     * @createDate: 2018/6/6 16:03
     * @version: 1.0
     */
    public static class FileUtils extends FileSystemUtils {
        /**
         * 创建临时文件
         *
         * @param inputStream
         * @param name        文件名
         * @param ext         扩展名
         * @return
         * @throws IOException
         */
        public static File createTmpFile(InputStream inputStream, String name,
                                         String ext) throws IOException {
            FileOutputStream fos = null;
            try {
                File tmpFile = File.createTempFile(name, '.' + (ext == null ? "tmp" : "txt"));
                //			tmpFile.deleteOnExit();
                fos = new FileOutputStream(tmpFile);
                int read = 0;
                byte[] bytes = new byte[1024 * 100];
                while ((read = inputStream.read(bytes)) != -1) {
                    fos.write(bytes, 0, read);
                }
                fos.flush();
                return tmpFile;
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                    }
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        }

        /**
         * 创建临时文件
         *
         * @param inputStream
         * @param filename
         * @throws IOException
         */
        public static File createTmpFile(InputStream inputStream, String filename) throws IOException {
            String ext = getExtension(filename, "");
            return createTmpFile(inputStream, newFileName(filename), ext);
        }

        /**
         * @param file
         * @throws Exception
         */
        public static void createFile(String file) {
            File f = new File(file);
            if (f.exists()) {
                // DO NOTHING
            } else {
                f.getParentFile().mkdirs();
                try {
                    f.createNewFile();
                } catch (IOException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }

        /**
         * UUID重命名文件名，避免中文问题及磁盘文件重名等问题
         *
         * @param originalFilename
         * @return
         */
        public static String newFileName(String originalFilename) {
            String ext = getExtension(originalFilename, "");
            return UUID.randomUUID().toString() + "." + ext;
        }

        /**
         * @param originalFilename
         * @return
         */
        public static String newFileNameWithDate(String originalFilename) {
            String ext = getExtension(originalFilename, "");
            return originalFilename.substring(0, originalFilename.lastIndexOf('.')) + DateUtils.thisDate() + "." + ext;
        }

        /**
         * @param originalFilename
         * @return
         */
        public static String newFileNameWithDateTime(String originalFilename) {
            String ext = getExtension(originalFilename, "");
            return originalFilename.substring(0, originalFilename.lastIndexOf('.')) + DateUtils.getCurrentTime() + "." + ext;
        }

        /**
         * @param filename
         * @param defExt
         * @return
         */
        public static String getExtension(String filename, String defExt) {
            if ((filename != null) && (filename.length() > 0)) {
                int i = filename.lastIndexOf('.');
                if ((i > -1) && (i < (filename.length() - 1))) {
                    return filename.substring(i + 1);
                }
            }
            return defExt;
        }

        public static String copyToString(InputStream in) throws IOException {
            if (in == null) {
                return "";
            }
            StringWriter out = new StringWriter();
            FileCopyUtils.copy(new InputStreamReader(in), out);
            return out.toString();
        }
    }
}
